﻿namespace NJQuery {
	using System;
	using System.ComponentModel;
	using System.Globalization;

	/// <summary>
	/// A complex type representing a horizontal position value that is absolutely defined as the left or relative to its container.
	/// </summary>
	[TypeConverter(typeof(HorizontalPositionConverter))]
	public struct HorizontalPosition {


		#region Static Readonly Fields: Standard values

		/// <summary>
		/// Specifies that the item is to be vorizontally centered.
		/// </summary>
		public static readonly HorizontalPosition Center = new HorizontalPosition();

		/// <summary>
		/// Specifies that the item is to be left aligned.
		/// </summary>
		public static readonly HorizontalPosition Left = new HorizontalPosition(HorizontalAlign.Left);

		/// <summary>
		/// Specifies that the item is to be right aligned.
		/// </summary>
		public static readonly HorizontalPosition Right = new HorizontalPosition(HorizontalAlign.Right);

		#endregion


		#region Constants

		private const HorizontalAlign absolute = (HorizontalAlign)(-1);

		#endregion


		#region Static Methods

		/// <summary>
		/// Converts a string representation to its <see cref="HorizontalPosition"/> equivalent.
		/// </summary>
		/// <param name="text">A <see cref="String"/> containing the value to convert.</param>
		/// <param name="result">The parsed value when the conversion succeeds, or a default instance otherwise.</param>
		/// <returns>Indicates whether the conversion succeeded.</returns>
		public static bool TryParse(string text, out HorizontalPosition result) {

			if(string.IsNullOrEmpty(text)) {
				result = Center;
				return false;
			}

			int leftPosition;
			if(int.TryParse(text, out leftPosition)) {
				result = new HorizontalPosition(leftPosition);
				return true;
			}

			//TODO: Write a Enum.TryParse!!!
			try {
				result = new HorizontalPosition((HorizontalAlign)Enum.Parse(typeof(HorizontalAlign), text));
				return true;
			} catch(ArgumentException) { }

			result = Center;
			return false;
		}

		#endregion


		#region Fields

		private readonly HorizontalAlign align;
		private readonly int topPosition;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes an instance of the <see cref="HorizontalPosition"/> type using an alignment.
		/// </summary>
		/// <param name="align">The alignment to use to calculate the horizontal position.</param>
		/// <exception cref="ArgumentException">Horizontal alignment not defined.</exception>
		public HorizontalPosition(HorizontalAlign align) {
			if(!Enum.IsDefined(typeof(HorizontalAlign), align)) throw new ArgumentException("Value not defined.", "align");

			this.align = align;
			topPosition = 0;
		}

		/// <summary>
		/// Initializes an instance of the <see cref="HorizontalPosition"/> type using an absolute position.
		/// </summary>
		/// <param name="topPosition">The absolute number of pixels to offset the top of the object.</param>
		public HorizontalPosition(int topPosition) {
			align = absolute;
			this.topPosition = topPosition;
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets the alignment to use to calculate the horizontal position, if applicable.
		/// </summary>
		public HorizontalAlign? Align {
			get { return align == absolute ? (HorizontalAlign?)null : align; }
		}

		/// <summary>
		/// Gets the absolute number of pixels to offset the top of the object, if applicable.
		/// </summary>
		public int? TopPosition {
			get { return align == absolute ? topPosition : (int?)null; }
		}

		/// <summary>
		/// Gets whether or not the value is absolute as opposed to aligned/relative.
		/// </summary>
		public bool IsAbsolute {
			get { return align == absolute; }
		}

		#endregion


		#region Methods: Object management

		/// <summary>
		/// Determines whether or not the specified object is equal to the current <see cref="HorizontalPosition"/> object.
		/// </summary>
		/// <param name="obj">The <see cref="Object"/> to compare.</param>
		/// <returns>Whether or not the provided object is equal.</returns>
		public override bool Equals(object obj) {
			if(obj is HorizontalPosition) return this == (HorizontalPosition)obj;
			if(obj is int) return this.topPosition == (int)obj && align == absolute;
			return false;
		}

		/// <summary>
		/// Determines whether or not the specified HorizontalPosition is equal to the current <see cref="HorizontalPosition"/>.
		/// </summary>
		/// <param name="other">The <see cref="HorizontalPosition"/> to compare.</param>
		/// <returns>Whether or not the provided value is equal.</returns>
		public bool Equals(HorizontalPosition other) {
			return this == other;
		}

		/// <summary>
		/// Serves as a hash function for <see cref="HorizontalPosition"/>.
		/// </summary>
		/// <returns>The hash value associated with the contained data.</returns>
		public override int GetHashCode() {
			//TODO: Have TheMuuj handle this
			return topPosition.GetHashCode() & align.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="String"/> that represents the current <see cref="HorizontalPosition"/> object.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			if(align == absolute) return topPosition.ToString(CultureInfo.InvariantCulture);
			return align.ToString();
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <returns>The client script value.</returns>
		public string ToClientSource() {
			if(align == absolute) return topPosition.ToClientSource();
			return align.ToClientSource();
		}

		#endregion


		#region Operators

		/// <summary>
		/// Allows an integer to be cast automatically as a <see cref="HorizontalPosition"/> absolute value.
		/// </summary>
		/// <param name="value">The <see cref="Int32"/> value to cast.</param>
		/// <returns>The cast HorizontalPosition.</returns>
		/// <exception cref="ArgumentOutOfRangeException">The value must be unsigned.</exception>
		public static explicit operator HorizontalPosition(int value) {
			return new HorizontalPosition(value);
		}

		/// <summary>
		/// Compares the equality of two <see cref="HorizontalPosition"/> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values have equal values.</returns>
		public static bool operator ==(HorizontalPosition value0, HorizontalPosition value1) {
			return value0.align == value1.align && value0.topPosition.Equals(value1.topPosition);
		}

		/// <summary>
		/// Compares the inequality of two <see cref="HorizontalPosition"/> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values have distinct values.</returns>
		public static bool operator !=(HorizontalPosition value0, HorizontalPosition value1) {
			return value0.align != value1.align || !value0.topPosition.Equals(value1.topPosition);
		}

		#endregion


	}

}